<?php

/**
 * 
 * 
 * 
 * 
 * @author deathemperor
 * @version $Id: Profiler.php 1534 2012-03-29 04:35:26Z locth $
 *
 */
class SGN_Profiler extends Zend_Controller_Plugin_Abstract
{
    /**
     * 
     * All profiler data can be set into this variable to be parsed later
     * 
     * @var array
     */
    private $_profilerData = array();
    /**
     * 
     * Debug & Profiler Configuration
     * 
     * 
     * @var Zend_Config
     */
    private $_config = null;
    /**
     * 
     * Instance to keep singleton
     * 
     * 
     * @var SGN_Profiler
     */
    private static $_profilerInstance = null;
    public $configCacheEnabled = true;
    /**
     * 
     * Destructor
     * 
     */
    public function __destruct()
    {
        $response = Zend_Controller_Front::getInstance()->getResponse();
        $header = $response->getHeaders();
        if (SGN_Application::isRegistered('disableDebug')) {
        	return;
        }
        if ($header[0]['name'] == 'Content-Type' && $header[0]['value'] == 'application/json') {
            if (SGN_Application::isRegistered('profiler')) {
                $output = SGN_Application::get('profiler')->profiler();
                $response->setHeader('sgn-header', $output);
//                 ChromePhp::log($output);
            }
        }
    }
    /**
     * Set Profiler Data
     * 
     * @param mixed $name
     * @param mixed $value
     */
    public function setProfilerData($name, $value, $group = '')
    {
        if ($group) {
            $this->_profilerData[$group][$name][] = $value;
            return;
        }
        $this->_profilerData[$name][] = $value;
    }
    /**
     * Get Profiler Data 
     * 
     * @param string $name
     * 
     * @return array
     */
    public function getProfilerData($name)
    {
        if ($name) {
            return $this->_profilerData[$name];
        }
        return $this->_profilerData;
    }
    /**
     * Gather all profiler data and put into an output 
     * 
     * 
     * @return string
     * 
     */
    public function profiler()
    {
    	$trace = debug_backtrace();
        // Database Profiler
        $sgn = new SGN_Application();
        $this->_config = SGN_Application::getConfig('sgn', 'debug');
        $display = 'display:none';
        if ($_SERVER['REMOTE_ADDR'] == '10.199.5.22') {
//             $display = 'display:block';
        }
        $output = ' <style type="text/css" media="all">
                    @import "' . (SGN_Application::isRegistered('res3Party') ? SGN_Application::get('res3Party') : '') . '/thickbox/css/thickbox.css";
                    @import "' . (SGN_Application::isRegistered('res3Party') ? SGN_Application::get('res3Party') : '') . '/debug/css/debug.css";
                    </style>
                    <script type="text/javascript">
	                    	var head = document.getElementsByTagName("head")[0];
                    	if(typeof jQuery == "undefined"){
	                    	var newelement = document.createElement("script");
	                    	newelement.src= "' . (SGN_Application::isRegistered('res3Party') ? SGN_Application::get('res3Party') : '') . '/jquery/jquery-1.7.1.min.js"; 
	                   		head.appendChild(newelement);
                   		}
                   		
                    	var newelement = document.createElement("script");
                    	newelement.src= "' . (SGN_Application::isRegistered('res3Party') ? SGN_Application::get('res3Party') : '') . '/thickbox/js/thickbox.js"; 
                   		head.appendChild(newelement);
                    	
                   		var newelement = document.createElement("script");
                    	newelement.src= "' . (SGN_Application::isRegistered('res3Party') ? SGN_Application::get('res3Party') : '') . '/debug/js/union_debug.js"; 
                   		head.appendChild(newelement);
    					
                    </script>
                    <div id="feedback">
                        <a href="#TB_inline?height=700&width=900&inlineId=debuginfo" class="thickbox">Debug info</a>
		            </div>
                    <div id="debuginfo" style=' . $display . '; margin-top:10px; text-align:left;;
                    background-color:white; color:black;height:300px;overflow:auto;width:100%"><div style="padding-left:30px">';
        if (SGN_Application::get('isAjax')) {
            // reset $output value
            $output = '';
        }
        $renderTime = sprintf('%01.3f', (microtime(true) - SGN_Application::get('pageStartTime')) * 1000);
        if ($renderTime > $this->_config->get('profiler')->get('pageRenderTimeThreshold')) {
            $renderTime = "<span class=\"red big\">$renderTime</span>";
        }
        if (SGN_Application::$params['xhprof']) {
            $xhprof = '(<b><a target="_blank" href="' . SGN_Application::get('siteUrl') . '/defaultXhprof/xhprof_html/index.php?run=' . $this->_profilerData['xhprof'][0] . '&source=' . $this->_profilerData['xhprof'][1] . "\" class=\"red\">XHPROF DATA</a></b>)";
        }
        if (SGN_Application::$params['writedebug']) {
            $output = '';
        }
        if (SGN_Application::isRegistered('profile')) {
            $userID = SGN_Application::get('profile')->getUserId();
        }
        // track clientscript version last changes
        $cache = SGN_Application::loadCacheInstance('sgn', 'global');
        $key = 'global.clientscript.version';
        $lastVersion = $cache->getCache($key);
        if ($lastVersion === false) {
            $lastVersion = array(SGN_Resource::getStaticFileVersion(), SGN_Application::timeFormat(SGN_Application::getCurrentTime(), true));
            $cache->setCache($key, $lastVersion);
        }
        if ($lastVersion[0] < SGN_Resource::getStaticFileVersion()) {
            // update last change
            $lastVersion = array(SGN_Resource::getStaticFileVersion(), SGN_Application::timeFormat(SGN_Application::getCurrentTime(), true));
            $cache->setCache($key, $lastVersion);
        }
        $output .= "<div>" . (SGN_Application::get('isAjax') ? 'AJAX "' . $_SERVER['REQUEST_URI'] . '" ' : '') . "Page Render time: $renderTime <i>(all numbers are in miliseconds)</i> $xhprof</div><br />
        		ZingID: " . SGN_Session::get('username') . "<br />
             	UserID: " . $userID . "<br /><br />
             	Server_IP: " . $_SERVER['SERVER_ADDR'] . "<br />
             	Client_IP: " . $_SERVER['REMOTE_ADDR'] . "<br /><br />
             	Session ID: " . session_id() . "<br /><br />
             	APPLICATION_ENV: " . APPLICATION_ENV . "<br />
             	Configuration Cache: " . ($this->configCacheEnabled ? '<span class="green">ON</span>' : '<span class="red">OFF</span>') . " <a target=\"_blank\" href=\"" . SGN_Application::get('siteUrl') . "/admin/utility/deleteapc\">(Clear)</a><br />
             	Clientscript version: <b>" . SGN_Resource::getStaticFileVersion() . "</b> (last version changed on $lastVersion[1]" . ")<br />
             	<a href=\"" . SGN_Application::get('siteUrl') . $_SERVER['REQUEST_URI'] . "?refreshcache=1\" style=\"text-decoration:underline;\">No cache page</a><br />
             	<a href=\"" . SGN_Application::get('siteUrl') . $_SERVER['REQUEST_URI'] . "?xhprof=1\" style=\"text-decoration:underline;\">Run with XHPROF</a><br />
             	<a href=\"" . SGN_Application::get('siteUrl') . $_SERVER['REQUEST_URI'] . "?apcCache=1\" style=\"text-decoration:underline;\">Run with APC enable</a>
             	<br />
        ";
        $output .= $this->_setupDbProfilerData($sgn);
        $output .= '<hr>';
        $output .= $this->_setupAPIProfilerData($sgn);
        $output .= '<hr>';
        $output .= $this->_setupThriftProfilerData($sgn);
        $output .= '<hr>';
        $output .= $this->_setupRESTProfilerData();
        $output .= '<hr>';
        $output .= $this->_setupCacheProfilerData($sgn);
        $output .= '<hr>';
        $output .= $this->_setupHSProfilerData($sgn);
        $output .= '<hr>';
        $output .= $this->_profilerData['ZingProfiler'][0];
        $output .= '</div></div>';
        if (SGN_Application::get('isAjax')) {
            $rgn = uniqid();
            $output = '<div id="ajaxprofiler' . $rgn . '" style="display:none">' . $output . '</div>
            <script type="text/javascript">
                $("#debuginfo").append($("#ajaxprofiler' . $rgn . '"));
                $("#ajaxprofiler' . $rgn . '").show();
            </script>
            ';
        } else {
            $output = "<div id=\"profiler\">$output</div>";
        }
        if (SGN_Application::$params['writedebug'] && SGN_Application::get('writeDebug')) {
            SGN_Log::writeLog(__CLASS__, __FUNCTION__.date('H:i:s', time()), $output, true, false, 'html');
        }
        return $output;
    }

    /**
     * 
     * SGN Profiler Factory method to reassure singleton
     * 
     * @return SGN_Profiler
     * 
     */
    public static function factory()
    {
        if (self::$_profilerInstance) {
            return self::$_profilerInstance;
        } else {
            self::$_profilerInstance = new self();
            return self::$_profilerInstance;
        }
    }

    /** 
     * 
     * Setting up profiler data for SOAP API
     * 
     * @return string 
     * 
     */
    private function _setupAPIProfilerData()
    {
        if (! $this->_profilerData['api']) {
            return;
        }
        $output = "\t<div class=\"APIprofiler\"><h4>API Statistics</h4>\n\t<ol>";
        foreach ($this->_profilerData['api'] as $request) {
            if ($request[1] > $this->_config->get('profiler')->get('APITimeThreshold')) {
                $request[1] = "<span class=\"red\">$request[1]</span>";
            }
            $output .= "\t\t<li>API: $request[0] - $request[1]</li>";
        }
        $output .= "\t</ol>\n\t</div>";
        return $output;
    }
    /**
     * 
     * Setting up profiler data for Thrift calls
     * 
     * @return string
     * 
     */
    private function _setupThriftProfilerData()
    {
        if (!$this->_profilerData['THRIFT']) {
            return;
        }
        $output = "\t<div class=\"REST_APIprofiler\"><h4>THRIFT_API Statistics</h4>\n\t<ol>";
        foreach ($this->_profilerData['THRIFT'] as $key => $thrift) {
            $thriftProfiler[$thrift['THRIFT']['host'] . '_' . $thrift['THRIFT']['port']][$key] = $thrift;
        }
        foreach ($thriftProfiler as $key =>$thrifts) {
            // same host & port
            $options = explode('_', $key);
            $output .= "\t\t<li><ol style=\"margin-left:10px\">THRIFT_API ON: <span class=\"red\">$options[0]:$options[1]</span>";
            foreach ($thrifts as $thrift) {
                if ($thrift['totalTime'] > $this->_config->get('profiler')->get('APITimeThreshold')) {
                    $thrift['totalTime'] = "<span class=\"red\">$thrift[totalTime]</span>";
                }
                $output .= "\t\t<li>\t$thrift[method] (" . serialize($thrift['args']) . ") - $thrift[totalTime]</li>";
            }
            $output .= "\t</ol></li>";
        }
        $output .= "\t</ol>\n\t</div>";
        return $output;
    }
    /**
     * 
     * Setting up profiler data for REST API
     * 
     * @return string
     * 
     */
    private function _setupRESTProfilerData()
    {
        if (! $this->_profilerData['REST_API']) {
            return;
        }
        $output = "\t<div class=\"REST_APIprofiler\"><h4>REST_API Statistics</h4>\n\t<ol>";
        foreach ($this->_profilerData['REST_API'] as $request) {
            if ($request[1] > $this->_config->get('profiler')->get('APITimeThreshold')) {
                $request[1] = "<span class=\"red\">$request[1]</span>";
            }
            $output .= "\t\t<li>REST_API: $request[0] - $request[1] - params " . serialize($request[2]) . "</li>";
        }
        $output .= "\t</ol>\n\t</div>";
        return $output;
    }

    /**
     * 
     * Prepare Cache Profiler Output
     * 
     * @return string
     * 
     */
    private function _setupCacheProfilerData()
    {
        $output = '';
        if ($this->_profilerData['cache']['save']) {
            foreach ($this->_profilerData['cache']['save'] as $saveKey) {
                $saveKeys .= "\t\t\t<li>$saveKey</li>\n";
            }
        }
        if ($this->_profilerData['cache']['load']) {
            foreach ($this->_profilerData['cache']['load'] as $loadKey) {
                $loadKeys .= "\t\t\t<li>$loadKey</li>\n";
            }
        }
        $output = "<div>
             <div class=\"cacheProfiler\"><h4>Cache Statistics</h4></div>
             Set: " . count($this->_profilerData['cache']['save']) . "
             Get: " . count($this->_profilerData['cache']['load']) . "<br />
             <b style='color:red;text-align:center'>SET CACHES</b><br /> \n\t\t<ol>\n" . $saveKeys . "\t\t</ol><br />
             <b style='color:green;text-align:center'>GET CACHES</b><br /> \n\t\t<ol>\n" . $loadKeys . "\t\t</ol>
        </div>";
        return $output;
    }

    /**
     * 
     * Prepare Database Profiler Output
     * 
     * @param SGN_Profiler $sgn
     * 
     * @return string
     * 
     */
    private function _setupDbProfilerData($sgn)
    {
        if (! $sgn->getDbInstances()) {
            return '<div class=\"databaseProfiler\"><h4>Database</h4></div><span class="green">NO DATABASE CONNECTION ESTABLISHED</span>';
        }
        $totalQueries = 0;
        $longestTime = '';
        $instances = $sgn->getDbInstances();
        foreach ($sgn->getDbInstances() as $dbInstanceKey => $dbInstance) {
            foreach ($dbInstance as $instancekey => $instance) {
                $profiler = $instance->getProfiler();
                $this->_profilerData[$dbInstanceKey . '::' . $instancekey]['totalQueries'] += $profiler->getTotalNumQueries();
                $color = 'yellow';
                if ($profiler->getTotalNumQueries() > $this->_config->get('profiler')->get('queryThreshold')) {
                    $color = 'red';
                }
                $output .= "<div class=\"databaseProfiler\"><h4>Database \"$instancekey\": <span class=\"$color\">" . $profiler->getTotalNumQueries() . "</span> queries. </h4>\n";
                $output .= "\t<ol class=\"queryList\">\n";
                $longestTime = '';
                $longestQuery = '';
                if (! $profiler->getQueryProfiles()) {
                    continue;
                }
//                pr($instance);
                foreach ($profiler->getQueryProfiles() as $query) {
                    if ($query->getElapsedSecs() > $longestTime) {
                        $longestTime = $query->getElapsedSecs() * 1000;
                        $longestQuery = $query->getQuery();
                    }
                    $queryTime = $query->getElapsedSecs() * 1000;
                    $color = 'green';
                    if ($queryTime > $this->_config->get('profiler')->get('queryTimeThreshold')) {
                        $color = 'red';
                    }
                    $output .= "\t\t<li>" . $query->getQuery() . " - " . "<span class=\"$color\">$queryTime</span>" . "</li>\n";
                    $this->_profilerData[$dbInstanceKey . '::' . $instancekey]['query'][] = $query->getQuery();
                }
                $output .= "\t</ol>\n\t<div class=\"red\">Longest: $longestQuery - " . $longestTime . "</div>\n</div>\n";
                 # pr($dbProfilerData, 0);
            }
        }
        return $output;
    }
    private function _setupHSProfilerData($sgn)
    {
        if (!$this->_profilerData['HandlerSocket']) {
            return;
        }
        $output = "\t<div class=\"REST_APIprofiler\"><h4>HandlerSocket Statistics</h4>\n\t<ol>";
        foreach ($this->_profilerData['HandlerSocket'] as $key => $items) {
            // same host & port
            $options = explode('_', $key);
            $output .= "\t\t<li><ol style=\"margin-left:10px\">HandlerSocket ON: <span class=\"red\">$options[0]:$options[1]</span>";
            foreach ($items as $item) {
                if ($item['totalTime'] > $this->_config->get('profiler')->get('APITimeThreshold')) {
                    $item['totalTime'] = "<span class=\"red\">$item[totalTime]</span>";
                }
                $output .= "\t\t<li>\t$item[method] (" . serialize($item['args']) . ") - $item[totalTime]</li>";
            }
            $output .= "\t</ol></li>";
        }
        $output .= "\t</ol>\n\t</div>";
        return $output;
    }
}